"""The server module.
It is a stripped down HTTP server that only replies to pomares specific requests."""

import threading, BaseHTTPServer, asyncore, asynchat, socket, re, select, time, exceptions, json
import log, pomarescgi, toc, serialize, config, talk

regex_GET = re.compile('^GET (/\S+) HTTP/...$')
regex_OPTIONS = re.compile('^(\S+)[:] ?(\S+)?')
regex_requested = re.compile('^(/?[a-zA-Z0-9_\-/]*/)?\?request=([a-zA-Z0-9_\-]+)(&.*)?$')
regex_requested_args = re.compile('&([a-zA-Z0-9_\-]+=[a-zA-Z0-9_\-]+)')

class Handler(asynchat.async_chat):
	"""The Handler class server is a channel to each client. 
	It will then parse and take care of the request."""
	def __init__(self, conn, server, map={}):
		log.log('Handler')
		self.map = map
		asynchat.async_chat.__init__(self, sock=conn, map=self.map)
		self.conn = conn
		self.buffer = []
		self.received = 0
		self.set_terminator("\r\n\r\n")
		self.ac_in_buffer_size = 2048
		self.updated = int(time.time())
		self.parsed = False
		self.parsed_options = None
		self.parsed_args = None
		self.parsed_request = None
		self.parsed_pomar = None
		self.requested = None
		self.server = server
		self.canread = True
		self.canwrite = True

	def readable(self):
		return self.canread

	def writeable(self):
		return self.canwrite

	def collect_incoming_data(self, data):
		log.log('collect_incoming_data')
		self.updated = int(time.time())

		self.received += len(data)
		if self.received < self.ac_in_buffer_size:
			self.buffer.append(data)
		else:
			self.handle_close()


	def found_terminator(self):
		log.log('found it!')
		self.canread = True
		self.parse_request()
		if self.parsed:
			#self.canread = False
			self.server.process_request(self)
			#self.close_when_done()
			#clean socket for more requests:
			self.buffer = []
			self.received = 0
		else:
			self.push_status(400, 'Bad Request')
			self.handle_close()

	def parse_request(self):
		_buff = self.buffer[0].split('\r\n')
		r = regex_GET
		requested = None
		if r.match(_buff[0]):
			requested = r.findall(_buff[0])[0]
			options = []
			r = regex_OPTIONS
			for b in _buff[1:]: 
				options.append(r.findall(b)[0])
			options =  dict(options)
			log.log('parsed_options: %s' % options)
			self.parsed_options = options
		
		log.log('requested: %s' % requested)
		self.requested = requested

		r = regex_requested 
		try:
			pomar, req, args = r.findall(requested)[0]
			self.parsed_pomar = pomar
			self.parsed_request = req
			log.log('parsed_pomar: %s' % pomar)
			log.log('parsed_request: %s' % req)
			if args != '':
				r = regex_requested_args

				parsed_args = {}
				for arg in r.split(args):
					if arg != '':
						key, val = re.split('=', arg)
						parsed_args[key]=val

				log.log('parsed_args: %s' % parsed_args)
				self.parsed_args = parsed_args
			else:
				self.parsed_args = {}

			self.parsed = True
					
		except:
			log.log('no match: %s' % self.requested)
			pass #dont really care about any exceptions if no match

	def push_status(self, code, status_msg, extra='', size=0):
		content_length = ''

		if size > 0:
			content_length = 'Content-Length: %d\r\n' % size

		self.push("HTTP/1.0 %d %s\r\n\
Content-Type: text/plain\r\n\
Server: Pomares/SVN\r\nPomares-ID: %s\r\n\
%s\
Connection: Keep-Alive\r\n\r\n%s"
		% (code, status_msg, config.my_uuid, content_length, extra )
		)

	
	def __del__(self):
		log.log('dying...')

class Server(asyncore.dispatcher, threading.Thread):
	"""The Server class is a stripped down HTTP server that only replies to pomares specific requests."""

	def __init__(self, port=8080, bind_address='', backlog=1024, map={}):
		threading.Thread.__init__(self)
		self.map = map
		asyncore.dispatcher.__init__(self, map=self.map)
		self.create_socket(socket.AF_INET, socket.SOCK_STREAM)
		self.set_reuse_addr()
		self.bind((bind_address, port))
		self.listen(backlog)


	def handle_accept(self):
		log.log('accept()')
		conn, addr = self.accept()	
		log.log('conn: %s, addr: %s' % (conn, addr))
		Handler(conn, self, map=self.map)

	def process_request(self, channel):
		log.log('parsed: %s' % channel.parsed)

		talk.to_client(channel)

	def run(self):
		connected=self.map
		while True:
			asyncore.loop(count=2, map=connected)

			"""let's get rid of all _zombie_ sockets (inactive for 60 seconds)"""
			try:
				for i in connected:
					if isinstance(connected[i], Handler):
						if int(time.time()) - connected[i].updated >= 60:
							connected[i].handle_close()
			except exceptions.RuntimeError:
				pass	

